/*
 * Funny Language - a free style programming language.
 * Copyright (C) 2015 by fanguangping (fanguangping@163.com)
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "runner_translate.h"

static void test_alloc_input() {
    Input *input = alloc_input();
    assert_non_null(input);
}

static void test_file_input() {
    FILE *file = tmpfile();
    Input *input = file_input(file);
    assert_non_null(input);
    assert_true(input->isFile);
    assert_non_null(input->file);
    fclose(file);
}

static void test_string_input() {
    Input *input = string_input("test");
    assert_non_null(input);
    assert_false(input->isFile);
    assert_non_null(input->string.buffer);
    assert_int_equal(input->string.pointer, 0);
}

static void test_get_char_from_file_input() {
    FILE *file = create_temp_file("abc");
    fseek(file, 0L, SEEK_SET);
    Input *input = file_input(file);
    assert_int_equal(get_char_from_input(input), 'a');
    assert_int_equal(get_char_from_input(input), 'b');
    assert_int_equal(get_char_from_input(input), 'c');
    assert_int_equal(get_char_from_input(input), EOF);
    fclose(file);
}

static void test_get_char_from_string_input() {
    Input *input = string_input("abc");
    assert_int_equal(get_char_from_input(input), 'a');
    assert_int_equal(get_char_from_input(input), 'b');
    assert_int_equal(get_char_from_input(input), 'c');
    assert_int_equal(get_char_from_input(input), EOF);
}

static void test_unget_char_from_file_input() {
    FILE *file = create_temp_file("abc");
    fseek(file, 0L, SEEK_END);
    Input *input = file_input(file);
    assert_int_equal(unget_char_from_input('c', input), 'c');
    assert_int_equal(unget_char_from_input('b', input), 'b');
    assert_int_equal(unget_char_from_input('a', input), 'a');
    assert_int_equal(unget_char_from_input(EOF, input), EOF);
}

static void test_unget_char_from_string_input() {
    Input *input = string_input("abc");
    input->string.pointer = 3;
    assert_int_equal(unget_char_from_input('c', input), 'c');
    assert_int_equal(unget_char_from_input('b', input), 'b');
    assert_int_equal(unget_char_from_input('a', input), 'a');
    assert_int_equal(unget_char_from_input(EOF, input), EOF);
}

static void test_char_to_string() {
    String str = char_to_string('a');
    assert_string_equal(str, "a");
}

static void test_has_next_true_case1() {
    Input *input = string_input("  abc  ");
    assert_true(has_next(input));
}

static void test_has_next_true_case2() {
    Input *input = string_input(";  comment\n  abc ");
    assert_true(has_next(input));
}

static void test_has_next_true_case3() {
    Input *input = string_input("  ,  abc ");
    assert_true(has_next(input));
}

static void test_has_next_true_case4() {
    Input *input = string_input("  ,  ;  comment\n  abc ");
    assert_true(has_next(input));
}

static void test_has_next_false_case1() {
    Input *input = string_input("");
    assert_false(has_next(input));
}

static void test_has_next_false_case2() {
    Input *input = string_input("  ");
    assert_false(has_next(input));
}

static void test_has_next_false_case3() {
    Input *input = string_input(";  comment");
    assert_false(has_next(input));
}

static void test_has_next_false_case4() {
    Input *input = string_input(";  comment1\n  ;  comment2\n  ;  comment3\n ");
    assert_false(has_next(input));
}

static void test_has_next_false_case5() {
    Input *input = string_input("  ,  ");
    assert_false(has_next(input));
}

static void test_has_next_false_case6() {
    Input *input = string_input("  ,  ;  comment\n  ");
    assert_false(has_next(input));
}

static void test_next_case1() {
    Input *input = string_input("");
    assert_null(next(input));
}

static void test_next_case2() {
    Input *input = string_input("\\abc");
    assert_string_equal(next(input), "\\abc");
}

static void test_next_case3() {
    Input *input = string_input("abc");
    assert_string_equal(next(input), "abc");
}

static void test_next_case4() {
    Input *input = string_input("abc{");
    assert_string_equal(next(input), "abc");
}

static void test_next_case5() {
    Input *input = string_input("abc}");
    assert_string_equal(next(input), "abc");
}

static void test_next_case6() {
    Input *input = string_input("abc[");
    assert_string_equal(next(input), "abc");
}

static void test_next_case7() {
    Input *input = string_input("abc]");
    assert_string_equal(next(input), "abc");
}

static void test_next_case8() {
    Input *input = string_input("abc,");
    assert_string_equal(next(input), "abc");
}

static void test_next_case9() {
    Input *input = string_input("{abc");
    assert_string_equal(next(input), "{");
}

static void test_next_case10() {
    Input *input = string_input("}abc");
    assert_string_equal(next(input), "}");
}

static void test_next_case11() {
    Input *input = string_input("[abc");
    assert_string_equal(next(input), "[");
}

static void test_next_case12() {
    Input *input = string_input("]abc");
    assert_string_equal(next(input), "]");
}

static void test_next_case13() {
    Input *input = string_input(",abc");
    assert_null(next(input));
}

static void test_next_case14() {
    Input *input = string_input(",");
    assert_null(next(input));
}

static void test_next_case15() {
    Input *input = string_input("\"abc\"");
    assert_string_equal(next(input), "\"abc\"");
}

static void test_read_symbol_case1() {
    Input *input = string_input("abc");
    input->string.pointer++;
    String symbol = read_symbol('a', input, FALSE);
    assert_string_equal(symbol, "abc");
}

static void test_read_symbol_case2() {
    Input *input = string_input("{abc}");
    input->string.pointer++;
    String symbol = read_symbol('{', input, FALSE);
    assert_string_equal(symbol, "");
}

static void test_read_symbol_case3() {
    Input *input = string_input("[abc]");
    input->string.pointer++;
    String symbol = read_symbol('[', input, FALSE);
    assert_string_equal(symbol, "");
}

static void test_read_symbol_case4() {
    Input *input = string_input("abc}");
    input->string.pointer++;
    String symbol = read_symbol('a', input, FALSE);
    assert_string_equal(symbol, "abc");
}

static void test_read_symbol_case5() {
    Input *input = string_input("abc]");
    input->string.pointer++;
    String symbol = read_symbol('a', input, FALSE);
    assert_string_equal(symbol, "abc");
}

static void test_read_symbol_case6() {
    Input *input = string_input("abc,");
    input->string.pointer++;
    String symbol = read_symbol('a', input, FALSE);
    assert_string_equal(symbol, "abc");
}

static void test_read_symbol_case7() {
    Input *input = string_input("abc;comment");
    input->string.pointer++;
    String symbol = read_symbol('a', input, FALSE);
    assert_string_equal(symbol, "abc");
}

static void test_read_symbol_case8() {
    Input *input = string_input("\\abc");
    input->string.pointer++;
    String symbol = read_symbol('\\', input, TRUE);
    assert_string_equal(symbol, "\\abc");
}

static void test_read_string() {
    Input *input = string_input("\" this is a string ; comment abc , 123 {} [] for test \" abc");
    input->string.pointer++;
    String string = read_string('"', input);
    assert_string_equal(string, "\" this is a string ; comment abc , 123 {} [] for test \"");
    assert_int_equal(input->string.pointer, strlen(string));
}

static void test_next_token_normal_statement() {
    Input *input = string_input(copy_string(STATEMENT));
    String token = NULL;
    token = next_token(input);
    assert_string_equal(token, "{");
    token = next_token(input);
    assert_string_equal(token, "define");
    token = next_token(input);
    assert_string_equal(token, "function");
    token = next_token(input);
    assert_string_equal(token, "{");
    token = next_token(input);
    assert_string_equal(token, "add");
    token = next_token(input);
    assert_string_equal(token, "}");
    token = next_token(input);
    assert_string_equal(token, ":");
    token = next_token(input);
    assert_string_equal(token, "{");
    token = next_token(input);
    assert_string_equal(token, "add");
    token = next_token(input);
    assert_string_equal(token, "{");
    token = next_token(input);
    assert_string_equal(token, "a");
    token = next_token(input);
    assert_string_equal(token, "}");
    token = next_token(input);
    assert_string_equal(token, "{");
    token = next_token(input);
    assert_string_equal(token, "b");
    token = next_token(input);
    assert_string_equal(token, "}");
    token = next_token(input);
    assert_string_equal(token, "}");
    token = next_token(input);
    assert_string_equal(token, "as");
    token = next_token(input);
    assert_string_equal(token, "{");
    token = next_token(input);
    assert_string_equal(token, "{");
    token = next_token(input);
    assert_string_equal(token, "a");
    token = next_token(input);
    assert_string_equal(token, "}");
    token = next_token(input);
    assert_string_equal(token, "+");
    token = next_token(input);
    assert_string_equal(token, "{");
    token = next_token(input);
    assert_string_equal(token, "b");
    token = next_token(input);
    assert_string_equal(token, "}");
    token = next_token(input);
    assert_string_equal(token, "}");
    token = next_token(input);
    assert_string_equal(token, "}");
    token = next_token(input);
    assert_null(token);
}

static void test_next_token_loop_statement() {
    Input *input = string_input(copy_string(LOOP_STATEMENT));
    String token = NULL;
    token = next_token(input);
    assert_string_equal(token, "{");
    token = next_token(input);
    assert_string_equal(token, "case");
    token = next_token(input);
    assert_string_equal(token, "[");
    token = next_token(input);
    assert_string_equal(token, "when");
    token = next_token(input);
    assert_string_equal(token, "{");
    token = next_token(input);
    assert_string_equal(token, "a");
    token = next_token(input);
    assert_string_equal(token, "}");
    token = next_token(input);
    assert_string_equal(token, "then");
    token = next_token(input);
    assert_string_equal(token, "{");
    token = next_token(input);
    assert_string_equal(token, "b");
    token = next_token(input);
    assert_string_equal(token, "}");
    token = next_token(input);
    assert_string_equal(token, "]");
    token = next_token(input);
    assert_string_equal(token, "else");
    token = next_token(input);
    assert_string_equal(token, "{");
    token = next_token(input);
    assert_string_equal(token, "c");
    token = next_token(input);
    assert_string_equal(token, "}");
    token = next_token(input);
    assert_string_equal(token, "}");
    token = next_token(input);
    assert_null(token);
}

int main(int argc, char **argv) {
    const struct CMUnitTest tests[] = {
            cmocka_unit_test(test_alloc_input),
            cmocka_unit_test(test_file_input),
            cmocka_unit_test(test_string_input),
            cmocka_unit_test(test_get_char_from_file_input),
            cmocka_unit_test(test_get_char_from_string_input),
            cmocka_unit_test(test_unget_char_from_file_input),
            cmocka_unit_test(test_unget_char_from_string_input),
            cmocka_unit_test(test_char_to_string),
            cmocka_unit_test(test_has_next_true_case1),
            cmocka_unit_test(test_has_next_true_case2),
            cmocka_unit_test(test_has_next_true_case3),
            cmocka_unit_test(test_has_next_true_case4),
            cmocka_unit_test(test_has_next_false_case1),
            cmocka_unit_test(test_has_next_false_case2),
            cmocka_unit_test(test_has_next_false_case3),
            cmocka_unit_test(test_has_next_false_case4),
            cmocka_unit_test(test_has_next_false_case5),
            cmocka_unit_test(test_has_next_false_case6),
            cmocka_unit_test(test_next_case1),
            cmocka_unit_test(test_next_case2),
            cmocka_unit_test(test_next_case3),
            cmocka_unit_test(test_next_case4),
            cmocka_unit_test(test_next_case5),
            cmocka_unit_test(test_next_case6),
            cmocka_unit_test(test_next_case7),
            cmocka_unit_test(test_next_case8),
            cmocka_unit_test(test_next_case9),
            cmocka_unit_test(test_next_case10),
            cmocka_unit_test(test_next_case11),
            cmocka_unit_test(test_next_case12),
            cmocka_unit_test(test_next_case13),
            cmocka_unit_test(test_next_case14),
            cmocka_unit_test(test_next_case15),
            cmocka_unit_test(test_read_symbol_case1),
            cmocka_unit_test(test_read_symbol_case2),
            cmocka_unit_test(test_read_symbol_case3),
            cmocka_unit_test(test_read_symbol_case4),
            cmocka_unit_test(test_read_symbol_case5),
            cmocka_unit_test(test_read_symbol_case6),
            cmocka_unit_test(test_read_symbol_case7),
            cmocka_unit_test(test_read_symbol_case8),
            cmocka_unit_test(test_read_string),
            cmocka_unit_test(test_next_token_normal_statement),
            cmocka_unit_test(test_next_token_loop_statement),
    };
    return cmocka_run_group_tests(tests, NULL, NULL);
}
